fix(llm): fail on an empty completion where it happens, and make the cap overridable - #512
Open
arthapraha wants to merge 1 commit into
Open
fix(llm): fail on an empty completion where it happens, and make the cap overridable#512arthapraha wants to merge 1 commit into
arthapraha wants to merge 1 commit into
Conversation
…cap overridable
litellm.py returned response.choices[0].message.content unchecked. That value
is None whenever the model produced no visible text -- most often a reasoning
model that spent its whole budget on reasoning_content and stopped with
finish_reason='length'. The None travelled into plan_execute.planner and
raised
TypeError: expected string or bytes-like object, got 'NoneType'
inside parse_plan's regex, four frames from the cause, blaming the parser for
the backend's result.
EmptyCompletionError names the model, the finish_reason and the token count,
and says how to raise the cap. An empty string is deliberately still a valid
answer; only None is treated as no completion.
max_tokens was hard-coded at 2048, which is comfortable for a non-reasoning
model and too tight for one whose visible answer is what remains after the
thinking is paid for. It now reads AOB_LLM_MAX_TOKENS and defaults to 2048, so
existing behaviour is unchanged.
llm suite 25/25 passing.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Attila <29815676+arthapraha@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
A completion with no visible content currently travels as
NonefromLiteLLMBackendintoparse_plan, where it reaches a regex and raises:The traceback names
_TASK_RE.finditer— four frames from the cause — andmentions neither
max_tokensnorfinish_reason. Full report in #511.Fix Details
Two changes, both small.
1. Raise where the fact is.
EmptyCompletionErroris raised inLiteLLMBackend.generate_with_usage, after the completion returns and beforeLLMResultis constructed — so aNonenever enters the system. The messagecarries the model,
finish_reason, tokens consumed and the cap:2. Make the cap overridable.
max_tokensreadsAOB_LLM_MAX_TOKENS,defaulting to the current 2048 — so behaviour is unchanged unless someone
changes it.
What this deliberately does not do: no retry, no fallback, no silent empty
string. Those are policy decisions for the project; this makes the failure
legible so the policy can be chosen with the facts visible.
Impact on Benchmarking
A run that previously died with a
TypeErrornow dies with anEmptyCompletionErrornaming the cause. No run that previously succeededbehaves differently — the raise fires only where
contentisNone, which isalready fatal today.
Related Issues
plan_executewith aTypeErrorthat blames the regex #511.Verification Steps
tests/integrationdoes not exist ate11d1c1. What was run instead, fromsrc/:mainate11d1c1: 19 passedthis branch: 25 passed (+6)
The six new tests, one per behaviour:
empty_content_raises_where_it_happenscontent=None,finish_reason='length'→ raises, cap namedempty_content_for_other_reasons_still_raiseslengthcaseordinary_completions_are_unaffectedempty_string_is_not_an_empty_completion""is a valid answer and must pass through — onlyNoneraisestoken_cap_is_overridableAOB_LLM_MAX_TOKENStakes effecttoken_cap_defaults_to_the_previous_valueThe 6 pre-existing failures in
src/evaluation/tests/are untouched — thisbranch does not import from
evaluation.AOB_LLM_MAX_TOKENSis read in-process and never crosses a spawn.os.environ.getatlitellm.py:49andlitellm.completionat:61are inthe same function in the same process,
litellmis imported in-process, andthere is no
subprocessorPopenanywhere undersrc/llm/. Checkeddeliberately: a fix introducing a new environment variable should not depend
on that variable surviving a process boundary.
Checklist
ruff check src/llm/— all checks passed;ruff format --check src/llm/— 9 files already formatted, on this branch. Unlike thescorer files, this path is clean both before and after.